home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * bsphere.c
- *
- * This module implements the bounding sphere calculations.
- *
- * from Persistence of Vision(tm) Ray Tracer
- * Copyright 1996 Persistence of Vision Team
- *---------------------------------------------------------------------------
- * NOTICE: This source code file is provided so that users may experiment
- * with enhancements to POV-Ray and to port the software to platforms other
- * than those supported by the POV-Ray Team. There are strict rules under
- * which you are permitted to use this file. The rules are in the file
- * named POVLEGAL.DOC which should be distributed with this file. If
- * POVLEGAL.DOC is not available or for more info please contact the POV-Ray
- * Team Coordinator by leaving a message in CompuServe's Graphics Developer's
- * Forum. The latest version of POV-Ray may be found there as well.
- *
- * This program is based on the popular DKB raytracer version 2.12.
- * DKBTrace was originally written by David K. Buck.
- * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
- *
- *****************************************************************************/
-
- #include "frame.h"
- #include "vector.h"
- #include "povproto.h"
- #include "bsphere.h"
-
-
-
- /*****************************************************************************
- * Local preprocessor defines
- ******************************************************************************/
-
- #define BRANCHING_FACTOR 4
-
-
-
- /*****************************************************************************
- * Local typedefs
- ******************************************************************************/
-
-
-
- /*****************************************************************************
- * Local variables
- ******************************************************************************/
-
- static int maxelements, Axis;
-
-
-
- /*****************************************************************************
- * Static functions
- ******************************************************************************/
-
- static void merge_spheres PARAMS((VECTOR C, DBL *r, VECTOR C1, DBL r1, VECTOR C2, DBL r2));
- static void recompute_bound PARAMS((BSPHERE_TREE *Node));
-
- static int find_axis PARAMS((BSPHERE_TREE **Elements, int first, int last));
- static void build_area_table PARAMS((BSPHERE_TREE **Elements, int a, int b, DBL *areas));
- static int sort_and_split PARAMS((BSPHERE_TREE **Root, BSPHERE_TREE **Elements, int *nElem, int first, int last));
-
- static int CDECL comp_elements PARAMS((CONST void *in_a, CONST void *in_b));
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * merge_spheres
- *
- * INPUT
- *
- * C, r - Center and radius^2 of new sphere
- * C1, r1, C2, r2 - Centers and radii^2 of spheres to merge
- *
- * OUTPUT
- *
- * C, r
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * Calculate a sphere that encloses two given spheres.
- *
- * CHANGES
- *
- * Jul 1994 : Creation.
- *
- * Oct 1994 : Added test for enclosed spheres. Calculate optimal sphere. [DB]
- *
- ******************************************************************************/
-
- static void merge_spheres(C, r, C1, r1, C2, r2)
- VECTOR C, C1, C2;
- DBL *r, r1, r2;
- {
- DBL l, r1r, r2r, k1, k2;
- VECTOR D;
- DBL rdiv;
-
- VSub(D, C1, C2);
-
- VLength(l, D);
-
- /* Check if one sphere encloses the other. */
-
- r1r = sqrt(r1);
- r2r = sqrt(r2);
-
- if (l + r1r <= r2r)
- {
- Assign_Vector(C, C2);
-
- *r = r2;
-
- return;
- }
-
- if (l + r2r <= r1r)
- {
- Assign_Vector(C, C1);
-
- *r = r1;
-
- return;
- }
-
- rdiv = (1.0 / l);
- k1 = (1.0 + (r1r - r2r) * rdiv) / 2.0;
- k2 = (1.0 + (r2r - r1r) * rdiv) / 2.0;
-
- /*
- k1 = (1.0 + (r1r - r2r) / l) / 2.0;
- k2 = (1.0 + (r2r - r1r) / l) / 2.0;
- */
-
- VLinComb2(C, k1, C1, k2, C2);
-
- *r = Sqr((l + r1r + r2r) / 2.0);
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * recompute_bound
- *
- * INPUT
- *
- * Node - Pointer to node
- *
- * OUTPUT
- *
- * Node
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * Recompute the bounding sphere of a given node in the bounding hierarchy,
- * i. e. find the bounding sphere that encloses the bounding spheres
- * of all nodes.
- *
- * NOTE: The sphere found is probably not the tightest sphere possible!
- *
- * CHANGES
- *
- * Jul 1994 : Creation.
- *
- * Oct 1994 : Improved bounding sphere calculation. [DB]
- *
- ******************************************************************************/
-
- static void recompute_bound(Node)
- BSPHERE_TREE *Node;
- {
- short i;
- DBL r2;
- VECTOR C;
-
- COOPERATE_1
-
- Assign_Vector(C, Node->Node[0]->C);
-
- r2 = Node->Node[0]->r2;
-
- for (i = 1; i < Node->Entries; i++)
- {
- merge_spheres(C, &r2, C, r2, Node->Node[i]->C, Node->Node[i]->r2);
- }
-
- Assign_Vector(Node->C, C);
-
- Node->r2 = r2;
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * comp_elements
- *
- * INPUT
- *
- * in_a, in_b - elements to compare
- *
- * OUTPUT
- *
- * RETURNS
- *
- * int - result of comparison
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * -
- *
- * CHANGES
- *
- * Oct 1994 : Creation. (Derived from the bounding slab creation code)
- *
- ******************************************************************************/
-
- static int CDECL comp_elements(in_a, in_b)
- CONST void *in_a;
- CONST void *in_b;
- {
- DBL am, bm;
-
- am = (*(BSPHERE_TREE **)in_a)->C[Axis];
- bm = (*(BSPHERE_TREE **)in_b)->C[Axis];
-
- if (am < bm - EPSILON)
- {
- return (-1);
- }
- else
- {
- if (am > bm + EPSILON)
- {
- return (1);
- }
- else
- {
- return (0);
- }
- }
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * find_axis
- *
- * INPUT
- *
- * OUTPUT
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * -
- *
- * CHANGES
- *
- * Oct 1994 : Creation. (Derived from the bounding slab creation code)
- *
- ******************************************************************************/
-
- static int find_axis(Elements, first, last)
- BSPHERE_TREE **Elements;
- int first, last;
- {
- int which = X;
- int i;
- DBL e, d = - BOUND_HUGE;
- VECTOR C, mins, maxs;
-
- Make_Vector(mins, BOUND_HUGE, BOUND_HUGE, BOUND_HUGE);
- Make_Vector(maxs, -BOUND_HUGE, -BOUND_HUGE, -BOUND_HUGE);
-
- for (i = first; i < last; i++)
- {
- Assign_Vector(C, Elements[i]->C);
-
- mins[X] = min(mins[X], C[X]);
- maxs[X] = max(maxs[X], C[X]);
-
- mins[Y] = min(mins[Y], C[Y]);
- maxs[Y] = max(maxs[Y], C[Y]);
-
- mins[Z] = min(mins[Z], C[Z]);
- maxs[Z] = max(maxs[Z], C[Z]);
- }
-
- e = maxs[X] - mins[X];
-
- if (e > d)
- {
- d = e; which = X;
- }
-
- e = maxs[Y] - mins[Y];
-
- if (e > d)
- {
- d = e; which = Y;
- }
-
- e = maxs[Z] - mins[Z];
-
- if (e > d)
- {
- which = Z;
- }
-
- return (which);
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * build_area_table
- *
- * INPUT
- *
- * OUTPUT
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * Generates a table of bounding sphere surface areas.
- *
- * CHANGES
- *
- * Oct 1994 : Creation. (Derived from the bounding slab creation code)
- *
- ******************************************************************************/
-
- static void build_area_table(Elements, a, b, areas)
- BSPHERE_TREE **Elements;
- int a, b;
- DBL *areas;
- {
- int i, imin, dir;
- DBL r2;
- VECTOR C;
-
- if (a < b)
- {
- imin = a; dir = 1;
- }
- else
- {
- imin = b; dir = -1;
- }
-
- Assign_Vector(C, Elements[a]->C);
-
- r2 = Elements[a]->r2;
-
- for (i = a; i != (b + dir); i += dir)
- {
- merge_spheres(C, &r2, C, r2, Elements[i]->C, Elements[i]->r2);
-
- areas[i-imin] = r2;
- }
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * sort_and_split
- *
- * INPUT
- *
- * OUTPUT
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * -
- *
- * CHANGES
- *
- * Oct 1994 : Creation. (Derived from the bounding slab creation code)
- *
- ******************************************************************************/
-
- static int sort_and_split(Root, Elements, nElem, first, last)
- BSPHERE_TREE **Root;
- BSPHERE_TREE **Elements;
- int *nElem, first, last;
- {
- int size, i, best_loc;
- DBL *area_left, *area_right;
- DBL best_index, new_index;
- BSPHERE_TREE *cd;
-
- Axis = find_axis(Elements, first, last);
-
- size = last - first;
-
- if (size <= 0)
- {
- return (1);
- }
-
- /*
- * Actually, we could do this faster in several ways. We could use a
- * logn algorithm to find the median along the given axis, and then a
- * linear algorithm to partition along the axis. Oh well.
- */
-
- QSORT((void *)(&Elements[first]), (size_t)size, sizeof(BSPHERE_TREE *), comp_elements);
-
- /*
- * area_left[] and area_right[] hold the surface areas of the bounding
- * boxes to the left and right of any given point. E.g. area_left[i] holds
- * the surface area of the bounding box containing Elements 0 through i and
- * area_right[i] holds the surface area of the box containing Elements
- * i through size-1.
- */
-
- area_left = (DBL *)POV_MALLOC(size * sizeof(DBL), "blob bounding hierarchy");
- area_right = (DBL *)POV_MALLOC(size * sizeof(DBL), "blob bounding hierarchy");
-
- /* Precalculate the areas for speed. */
-
- build_area_table(Elements, first, last - 1, area_left);
- build_area_table(Elements, last - 1, first, area_right);
-
- best_index = area_right[0] * (size - 3.0);
-
- best_loc = - 1;
-
- /*
- * Find the most effective point to split. The best location will be
- * the one that minimizes the function N1*A1 + N2*A2 where N1 and N2
- * are the number of objects in the two groups and A1 and A2 are the
- * surface areas of the bounding boxes of the two groups.
- */
-
- for (i = 0; i < size - 1; i++)
- {
- new_index = (i + 1) * area_left[i] + (size - 1 - i) * area_right[i + 1];
-
- if (new_index < best_index)
- {
- best_index = new_index;
- best_loc = i + first;
- }
- }
-
- POV_FREE(area_left);
- POV_FREE(area_right);
-
- /*
- * Stop splitting if the BRANCHING_FACTOR is reached or
- * if splitting stops being effective.
- */
-
- if ((size <= BRANCHING_FACTOR) || (best_loc < 0))
- {
- cd = (BSPHERE_TREE *)POV_MALLOC(sizeof(BSPHERE_TREE), "blob bounding hierarchy");
-
- cd->Entries = (short)size;
-
- cd->Node = (BSPHERE_TREE **)POV_MALLOC(size*sizeof(BSPHERE_TREE *), "blob bounding hierarchy");
-
- for (i = 0; i < size; i++)
- {
- cd->Node[i] = Elements[first+i];
- }
-
- recompute_bound(cd);
-
- *Root = cd;
-
- if (*nElem > maxelements)
- {
- /* Prim array overrun, increase array by 50%. */
-
- maxelements = 1.5 * maxelements;
-
- /* For debugging only. */
-
- Debug_Info("Reallocing elements to %d\n", maxelements);
-
- Elements = (BSPHERE_TREE **)POV_REALLOC(Elements, maxelements * sizeof(BSPHERE_TREE *), "bounding slabs");
- }
-
- Elements[*nElem] = cd;
-
- (*nElem)++;
-
- return (1);
- }
- else
- {
- sort_and_split(Root, Elements, nElem, first, best_loc + 1);
-
- sort_and_split(Root, Elements, nElem, best_loc + 1, last);
-
- return (0);
- }
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * Build_Bounding_Sphere_Hierarchy
- *
- * INPUT
- *
- * nElem - number of elements in Elements
- * Elements - array containing nElem elements
- *
- * OUTPUT
- *
- * Root - root node of the hierarchy
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * Create the bounding sphere hierarchy for a given set of elements.
- * One element consists of an element number (index into the array
- * of elements; e.g. blob components, triangles) and a bounding
- * sphere enclosing this element (center and squared radius).
- *
- * CHANGES
- *
- * Oct 1994 : Creation. (Derived from the bounding slab creation code)
- *
- ******************************************************************************/
-
- void Build_Bounding_Sphere_Hierarchy(Root, nElem, Elements)
- int nElem;
- BSPHERE_TREE **Root, **Elements;
- {
- int low, high;
-
- /*
- * This is a resonable guess at the number of elements needed.
- * This array will be reallocated as needed if it isn't.
- */
-
- maxelements = 2 * nElem;
-
- /*
- * Do a sort on the elements, with the end result being
- * a tree of objects sorted along the x, y, and z axes.
- */
-
- if (nElem > 0)
- {
- low = 0;
- high = nElem;
-
- while (sort_and_split(Root, Elements, &nElem, low, high) == 0)
- {
- low = high;
- high = nElem;
- }
- }
- }
-
-
-
- /*****************************************************************************
- *
- * FUNCTION
- *
- * Destroy_Bounding_Sphere_Hierarchy
- *
- * INPUT
- *
- * Node - Pointer to current node
- *
- * OUTPUT
- *
- * Node
- *
- * RETURNS
- *
- * AUTHOR
- *
- * Dieter Bayer
- *
- * DESCRIPTION
- *
- * Destroy bounding sphere hierarchy.
- *
- * CHANGES
- *
- * Aug 1994 : Creation.
- *
- * Dec 1994 : Fixed memory leakage. [DB]
- *
- ******************************************************************************/
-
- void Destroy_Bounding_Sphere_Hierarchy(Node)
- BSPHERE_TREE *Node;
- {
- short i;
-
- if (Node != NULL)
- {
- if (Node->Entries > 0)
- {
- /* This is a node. Free sub-nodes. */
-
- for (i = 0; i < Node->Entries; i++)
- {
- Destroy_Bounding_Sphere_Hierarchy(Node->Node[i]);
- }
-
- POV_FREE(Node->Node);
- }
-
- POV_FREE(Node);
- }
- }
-